// This document is for defining data api.
dojo.provide("spaghetti.dataset");

dojo.require("spaghetti.utility");

//---------------------------------------
// Interfaces define.

//=======================================
// For record.

dojo.declare("spaghetti.dataset.Record", null, {
	//	summary:
	//		This is the record mixin for data object. Every record must be attached to a dataset.
	//		Because data object can be anything(xml node, javascript object, remote object, etc). 
	//		So the record mixin doesn't has the ability to read or write its property. The dataset
	//		has the ability, and this design grant dataset the ability to track every change in the
	//		record. Every record out of dataset, must mixin this record mixin.
	
	/*Object*/ get : function(/*String*/ property){
		//	summary:
		//		Get the property in this record. The property can be anything.
		//
		//	property:
		//		The property name

		// Let the dataset to get the property.
		return this.dataset.getProperty(this, property);
	},
	
	set : function(/*String*/ property, /*Object*/ value){
		//	summary:
		//		Set the property of this record. The record can fire a onChange event.
		//
		//	property:
		//		The property name.
		//
		//	value:
		//		The property value.
		
		// If the property name is null. Ignore it.
		if(spaghetti.isNull(property))
			return ;
			
		// Fetch the old value.
		var oldValue = this.get(property);
		
		// Let the dataset to set the property.
		this.dataset.setProperty(this, property, value);
		
		// Fire the onChange event.
		this.onChange(property, oldValue, value);
	},

	/*String[]*/ getPropertyNames : function(){
		//	summary:
		//		Get all the property names in this record.
		
		// Leave the work for dataset, of course, dataset can use its metadata
		// to implement this.
		return this.dataset.getPropertyNames(this);
	},
	
	onChange : function(/*String*/ property, /*Object*/ oldValue, /*Object*/ newValue){
		//	summary:
		//		The onChange event for this record. It fires when the property of this
		//		record changed.
	}
});

//=======================================
// For Metadata.

dojo.declare("spaghetti.dataset.ValidateException", null, {
	//	summary:
	//		If the validator failed to validate the value. Throw this excepiton.
	
	constructor : function(/*String*/ message){
		this.message = message;
	}	
});

dojo.declare("spaghetti.dataset.Validator", null, {
	//	summary:
	//		The validattor interface for all the validators.
	
	validate : function(/*Object*/ value) /*Throws ValidateExcetion*/{
		//	summary:
		//		Validate the value.	
		spaghetti.notImplement("validate");
	}
});

dojo.declare("spaghetti.dataset.metadata.Constraint", null, {
	//	summary:
	//		The constraint metadata for data. For example, identifier, validation for record's
	//		property.
	
	/*Boolean*/ isIdentifier : function(){
		//	summary:
		//		Is this property the identifier of the record?
		
		spaghetti.notImplement("isIdentifier");
	},
	
	/*Validator*/ getValidator : function(){
		//	summary:
		//		Get the validator for this property, can be null. If the validator is null, never
		//		validate the value.
		
		spaghetti.notImplement("getValidator");
	}
});

dojo.declare("spaghetti.dataset.metadata.api", null, {
	//	summary:
	//		The metadata api for client data processing. Mostly, it is provide by the dataset.
	
	/*String[]*/ getPropertyNames : function(/*Record || null*/ record){
		//	summary:
		//		Get all the property names of this record. Mostly the properties only
		//		depends on dataset, not depends on the record. Not take this for granted.
		
		spaghetti.notImplement("getPropertyNames");
	},
	
	/*Object*/ getMeta : function(/*String*/ key){
		//	summary:
		//		This is the api for other meta data of the dataset. For example, if the dataset is a
		//		subset of the whole resultset, it will hold the begin index of this dataset, and the 
		//		whole size of the resultset. If the datasource support, the dataset can be even better,
		//		it can support the meta data as current_page_index, page_row_count and page_count etc.
		//		Other meta data can be supported, depends on the datasource.
		
		spaghetti.notImplement("getMeta");
	},
	
	/*String*/ getPropertyType : function(/*String*/ property, /*Record || null*/ record){
		//	summary:
		//		Get the type for the property of the record. Mostly the property only
		//		depends on dataset.
		//
		//	property: String
		//		The property name
		//
		//	return: String
		//		The property type. Such as Object, Date, String etc., according to the protocol.
				
		spaghetti.notImplement("getPropertyType");
	},
	
	/*Constraint*/ getPropertyConstraint : function(/*String*/ property, /*Record || null*/ record){
		//	summary:
		//		Get the type for the property of the record. Mostly the property only
		//		depends on dataset.
		//
		//	property:
		//		The property name.
				
		spaghetti.notImplement("getPropertyConstraint");
	}
});

//=======================================
// For Dataset.

dojo.declare("spaghetti.dataset.cursor.api", null, {
	//	summary:
	//		The data cursor for dataset. It can be get by a query to dataset. It point to a set of 
	//		records.
	
	/*Record*/ current : function(){
		//	summary:
		//		Get the current record this cursor pointing.
		
		spaghetti.notImplement("current");	
	},
	
	/*Cursor*/ next: function(){
		//	summary:
		//		Point to next record. If is the last record. Never move. Return the Cursor for shorthand.
		
		spaghetti.notImplement("next");
	},
	
	/*Cursor*/ prev: function(){
		//	summary:
		//		Point to the preveious record. If is the first record. Never move.
		
		spaghetti.notImplement("prev");
	},
	
	/*Boolean*/ hasNext : function(){
		//	summary:
		//		Return true if the cursor can move next.
		
		spaghetti.notImplement("hasNext");
	},
	
	/*Boolean*/ hasPrev : function(){
		//	summary:
		//		Return true if the cursor can move preveious.
		
		spaghetti.notImplement("hasPrev");
	}
});

dojo.declare("spaghetti.dataset.api", spaghetti.dataset.metadata.api, {
	//	summary:
	//		The dataset api. The dataset is an array of recrods. The record can be find by index.
	
	isRecord : function(/*Record*/ record){
		//	summary:
		//		If the record is a valid record in this dataset.
		
		spaghetti.notImplement("isRecord");
	},
	
	/*Record*/ getRecord : function(/*Number*/ index){
		//	summary:
		//		Get the record by index. 
		//
		//	index: Integer
		//		The index of the record.
		
		spaghetti.notImplement("getRecord");
	},
	
	addRecord : function(/*Object*/ data, /*Number*/ index){
		//	summary:
		//		Add a record. Note that the record is create by the dataset from your data.
		//		The data must follow some structure that defined by the dataset. Different
		//		dataset, different data structure.
		//	
		//	data:
		//		The data that to create record.
		//	
		//	index:
		//		The index of the new record. If index is undefined, push the new record at the bottom.
		
		spaghetti.notImplement("addRecord");
	},
	
	removeRecord : function(/*Record || Number*/ record){
		//	summary:
		//		Remove the record, by it or by its index.
		
		spaghetti.notImplement("removeRecord");
	},
	
	/*Object*/ getProperty : function(/*Record*/ record, /*String*/ property){
		//	summary:
		//		Get the property of the record. The interface function for different implementations.
		//
		//	record: Record
		//		The record.
		//
		//	property: String
		//		The property name.
		
		spaghetti.notImplement("getProperty");
	},
	
	setProperty : function(/*Record*/ record, /*String*/ property, /*Object*/ value)/*Throws ValidationException*/{
		//	sumamry:
		//		Set the property of the record. Same as the getProperty. If the record's property has
		//		validator, use the validator to validate the value. 
		//
		//	throws: ValidationException
		//		If failed to validate, throw this exception.
		
		spaghetti.notImplement("setProperty");
	},
	
	/*Number*/ indexOfRecord : function(/*Record*/ record){
		//	summary:
		//		Return the index of the record. If the record is not a validate record of this dataset,
		//		return -1
		
		spaghetti.notImplement("containsRecord");
	},
	
	/*Number*/ getRecordCount : function(){
		//	summary:
		//		Return the record count of this dataset.
		
		spaghetti.notImplement("getRecordCount");
	},

	/*Cursor*/ query : function(/*Object || null*/ query){
		//	summary:
		//		Query in this dataset.  Mostly is the criteria of the record. Or constraints. 
		//	
		//	example:
		//	|	{ age : 26, name : "Jack"} or { begin : 10, end : 20, filter : funciton(index){ return index % 2; } } 
		//	|	or { index : 10 }. 
		//		The query return a cursor of the resultset. If the query is null, the resultset 
		//		is all the records in this dataset. Begin with index 0.
		
		spaghetti.notImplement("query");
	},
	
	/*Boolean*/ isDirty : function(){
		//	summary:
		//		Return true f the data in this dataset changed.
		
		spaghetti.notImplement("isDirty");
	},
	
	//---------------------------------------------
	// Events
	onRecordAdd : function(/*Record*/ record, /*Number*/ index){
		//	summary:
		//		Fire when a record is inserted into this dataset.
		//
		//	record: Record
		//		The record added.
		//
		//	index: Integer
		//		The index of the new record.
	},
	
	onRecordChange : function(/*Record*/ record, /*String*/ property, /*Object*/ oldValue, /*Object*/ newValue){
		//	summary:
		//		Fire when a record in this dataset is changeed.
		//
		//	record: Record
		//		The changed record.
		//
		//	property: String
		//		The property name.
		//
		//	oldValue: Object
		//		The old value for the property.
		//
		//	newValue: Object
		//		The new value for the property.
	},
	
	onRecordRemove : function(/*Record*/ record){
		//	summary:
		//		Fire when a record in this dataset is removed.
	}
});